#define _GNU_SOURCE
#include <math.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <gio/gio.h>
#include <glib.h>
#include <stdbool.h>
#include "serial.h"
typedef int (*func_send)(void *, const uint8_t *, size_t, void *);
static void base_print_hex(const char *pre, const uint8_t *buffer,
    size_t size)
{
  gsize i;

  g_print("%s", pre);
  for(i = 0; i < size; i++) {
    g_print("%02x ", (unsigned char)buffer[i]);
  }
  g_print("\n");
}
static void print_hex(const char *pre, const char *next,
    const uint8_t *buffer, size_t size)
{
  g_print("%s ", pre);
  base_print_hex(next, buffer, size);
}
#define SERIAL_BUFFER_MAX 1024
int serial_open(const char *node, int baud, uint8_t data_bit,
    char parity, uint8_t stop_bit)
{
	int rc = 0, fd = 0;
  struct termios tios;

  fd = open(node, O_RDWR|O_NOCTTY| O_NONBLOCK);
	if (fd < 0) {
		printf("[error]filename:%s open error\n", node);
		rc = fd;
		goto out;
	}
  tcgetattr(fd, &tios);
  speed_t baudrate;
  switch (baud) {
		case 50:
			baudrate = B50;
			break;
		case 150:
			baudrate = B150;
			break;
		case 110:
			baudrate = B110;
			break;
		case 300:
			baudrate = B300;
			break;
		case 600:
			baudrate = B600;
			break;
		case 1200:
			baudrate = B1200;
			break;
		case 2400:
			baudrate = B2400;
			break;
		case 4800:
			baudrate = B4800;
			break;
		case 9600:
			baudrate = B9600;
			break;
		case 19200:
			baudrate = B19200;
			break;
		case 38400:
			baudrate = B38400;
			break;
		case 57600:
			baudrate = B57600;
			break;
		case 115200:
			baudrate = B115200;
			break;
		case 460800:
			baudrate = B460800;
			break;
		default:
			baudrate = B9600;
  }
  if ((cfsetispeed(&tios, baudrate) < 0) ||(cfsetospeed(&tios, baudrate) < 0)) {
    close(fd);
		goto out;
  }
  tios.c_cflag |=(CREAD | CLOCAL);
  tios.c_cflag &= ~CSIZE;
  switch (data_bit) {
		case 5:
			tios.c_cflag |= CS5;
			break;
		case 6:
			tios.c_cflag |= CS6;
			break;
		case 7:
			tios.c_cflag |= CS7;
			break;
		case 8:
		default:
			tios.c_cflag |= CS8;
			break;
  }
  if (stop_bit == 1)
		tios.c_cflag &=~ CSTOPB;
  else
		tios.c_cflag |= CSTOPB;
  if (parity == 'N') {
    tios.c_cflag &=~ PARENB;
  } else if (parity == 'E') {
    tios.c_cflag |= PARENB;
    tios.c_cflag &=~ PARODD;
  } else {
    tios.c_cflag |= PARENB;
    tios.c_cflag |= PARODD;
  }
  tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
  if (parity == 'N') {
    tios.c_iflag &= ~INPCK;
  } else {
    tios.c_iflag |= INPCK;
  }
  tios.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL);
  tios.c_iflag |= IGNBRK;
  tios.c_iflag |= IGNPAR;
  tios.c_oflag &=~ OPOST;
  tios.c_cc[VMIN] = 0;
  tios.c_cc[VTIME] = 0;
  if (tcsetattr(fd, TCSANOW, &tios) < 0) {
    close(fd);
    printf("[error] init\n");
    goto out;
  }
  g_debug("L%d f-%s %d[%s]baud:%d,data_bit:%d,parity:%d,stop_bit:%d\n",
      __LINE__, __func__,
			fd, node, baud, data_bit, parity, stop_bit);
	rc = fd;
out:
	return rc;
}
void *virtual_serial_open(int baud, int data_bit, int parity,
    int stop_bit, void *user_data)
{
  int fd = 0;
  char pts_name[256];
  GObject *serial = (GObject *)serial_serial_new();
  GIOChannel *channel = NULL;
  GError *error = NULL;

  g_object_set(serial, "nameAlias", "/dev/ptmx", "baud", baud,
      "dataBit", data_bit, "stopBit", stop_bit, "parity", parity,
      NULL);
  fd = serial_open("/dev/ptmx", baud, data_bit, 'N', stop_bit);
  if (fd < 0) {
    perror("Failed to open master device");
  }
  if (grantpt(fd) != 0 || unlockpt(fd) != 0 || ptsname_r(fd, pts_name, sizeof(pts_name)) != 0) {
    perror("Failed to grant/unlock pts");
  }
  g_debug("L%d f-%s name:%s\n", __LINE__, __func__, pts_name);
  g_object_set(serial, "name", pts_name, "port", fd, NULL);
  channel = g_io_channel_unix_new(fd);
  if (channel == NULL) {
    g_print("Failed to open the serial port\n");
  } else {
    g_io_channel_set_encoding(channel, NULL, &error);
    if (error != NULL) {
      g_warning("L%d f-%s %s\n", __LINE__, __func__, error->message);
      g_clear_error(&error);
    } else {
      g_debug("L%d f-%s channel:%p", __LINE__, __func__, channel);
    }
  }
  g_object_set(serial, "channel", channel, NULL);
  return serial;
}
/*
char *virtual_serial_get_node(void *serial)
{
  int fd = 0;
  char pts_name[256], *res = NULL;

  fd = g_io_channel_unix_get_fd(serial);
  if (ptsname_r(fd, pts_name, sizeof(pts_name)) != 0) {
    perror("Failed to grant/unlock pts");
  } else {
    res = g_strdup(pts_name);
  }

  return res;
}
*/
gboolean serial_read_to_tcp(GIOChannel *channel,
    GIOCondition condition, gpointer user_data)
{
  int ret = false;
  gchar buffer[1024];
  gsize len;
  GError *error = NULL;
  GObject *serial = user_data;

  g_debug("L%d f-%s", __LINE__, __func__);
 // conn = user_data;
#if 1
  g_io_channel_read_chars(channel, buffer, sizeof(buffer), &len, &error);
#else
  len = read(g_io_channel_unix_get_fd(channel), buffer, sizeof(buffer) -1);
#endif
  if (error != NULL) {
    g_warning("L%d f-%s %s\n", __LINE__, __func__, error->message);
    g_clear_error(&error);
    return FALSE;
  }
  
  if (len) {
    print_hex(__func__, "++++", (uint8_t *)buffer, len);
    if (serial) {
      func_send fsend;
      gpointer conn, func;
      g_object_get(serial, "funcSend", &func, "conn", &conn, NULL); 
      fsend = func;
      if (fsend) {
        ret = fsend(conn, (uint8_t *)buffer, len, NULL);
      }
    }
  }
  return ret;
}
gboolean serial_read_to_udp(GIOChannel *channel,
    GIOCondition condition, gpointer user_data)
{
  int ret = false;
  gchar buffer[1024];
  gsize len;
  GError *error = NULL;
  GObject *serial = user_data;

  g_debug("L%d f-%s", __LINE__, __func__);
#if 1
  g_io_channel_read_chars(channel, buffer, sizeof(buffer), &len, &error);
#else
  len = read(g_io_channel_unix_get_fd(channel), buffer, sizeof(buffer) -1);
#endif
  if (error != NULL) {
    g_warning("L%d f-%s %s\n", __LINE__, __func__, error->message);
    g_clear_error(&error);
    return FALSE;
  }
  
  if (len) {
    print_hex(__func__, "++++", (uint8_t *)buffer, len);
    if (serial) {
      func_send fsend;
      gpointer conn, func;
      g_object_get(serial, "send", &func, "conn", &conn, NULL); 
      fsend = func;
      if (fsend)
        ret = (int)fsend(conn, (uint8_t *)buffer, len, NULL);
    }
  }
  return ret;
}
void *serial_to_udp(void *serial, void *inout)
{
  g_io_add_watch(serial, G_IO_IN, serial_read_to_udp, inout);
  return NULL;
}
/*
int serial_read_to_udp_handle(GIOChannel *channel,
    GIOCondition condition, gpointer user_data)
{
  gsize len;
  GError *error = NULL;
  GSocket *socket = NULL;
  GObject *self = user_data;
  gchar *buffer;
  gint bufferSize;

  g_object_get(self, "buffer", &buffer, "bufferSize", &bufferSize,
      "conn",  &socket, NULL);
  g_debug("L%d f-%s", __LINE__, __func__);
  g_io_channel_read_chars(channel, buffer, bufferSize, &len,
      &error);
  if (error != NULL) {
    g_warning("L%d f-%s %s\n", __LINE__, __func__, error->message);
    g_clear_error(&error);
    return FALSE;
  }
  
  if (len) {
    print_hex(__func__, "++++", (uint8_t *) buffer, len);
    if (socket) {
      void *funcPrefixSend, *func;
      func_send fsend;
      g_object_get(self, "funcPrefixSend", &funcPrefixSend,
          "funcSend", &func, NULL);
      fsend = func;
      if (funcPrefixSend) {
        uint8_t *new_data = NULL;
        size_t new_size = 0;
        new_data = funcPrefixSend(buffer, &len, NULL);
        new_size = len;
        if (fsend)
          fsend(socket, (char *)new_data, new_size, addr);
      } else {
        if (fsend)
          fsend(socket, (char *)new_data, new_size, addr);
      }
    }
  }
  return TRUE;
}
*/
char *serial_driver__get_real_node(const char *name)
{
  char *str = NULL;

  if (!strcmp(name, "485-1")) {
    str = g_strdup("/dev/ttyRS485-5");
  } else if (!strcmp(name, "485-2")) {
    str = g_strdup("/dev/ttyRS485-1");
  } else if (!strcmp(name, "485-3")) {
    str = g_strdup("/dev/ttyRS485-6");
  } else if (!strcmp(name, "485-4")) {
    str = g_strdup("/dev/ttyRS485-3");
  } else if (!strcmp(name, "485-5")) {
    str = g_strdup("/dev/ttyRS485-2");
  } else if (!strcmp(name, "485-6")) {
    str = g_strdup("/dev/ttyRS485-0");
  } else if (!strcmp(name, "485-7")) {
    str = g_strdup("/dev/ttyRS485-7");
  } else if (!strcmp(name, "485-8")) {
    str = g_strdup("/dev/ttyRS485-4");
  } else if (!strcmp(name, "/dev/ttyUSB0")) {
    str = g_strdup(name);
  } else {
    g_warning("invalid name:%s", name);
  }
  return str;
}
uint8_t *serial_driver__str_to_hex(const char *content, size_t *size_p)
{
  uint8_t *hex = NULL;
  gchar **str_p = NULL;
  gsize len, i;

  str_p = g_strsplit(content, " ", 0);
  len = strlen(content) / 3;
  if (strlen(content) % 3) {
    len++;
  }
  hex = g_new(guint8, len);
  for(i = 0; i < len; i++) {
    hex[i] = g_ascii_strtoll(str_p[i], NULL, 16);
  //  g_print("%02x ", hex[i]);
  }
  g_strfreev(str_p);
  *size_p = len;
  return hex;
}
void serial_driver__print_hex2(const char *pre, const char *next,
    const uint8_t *buffer, size_t size)
{
  g_print("%s ", pre);
  base_print_hex(next, buffer, size);
}
